# -*- coding: UTF-8 -*-
# Author:JackyMao
# Blog:https://blog.csdn.net/xiaokai1999
import io
import sys
from typing import List
from urllib.parse import urlencode
import requests
import json
import re
from music163 import Music163,Encrypt
import argparse
import os
import time
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8') #改变标准输出的默认编码
class MusicDownLoad:
    Download_path = None                        # 本地下载地址
    music_url = None                            # music 链接
    music_title = None                          # music 标题
    music_author = None                         # music 作者
    music_lrc = None                            # music 歌词
    music_download_url = None                   # music 下载链接
    music_pic_url = None                        # music 图片链接
    req_url = "http://www.eggvod.cn/newmusic/"  # music 下载接口
    
    def __init__(self,username,password):
        self.userid, self.session = Music163().Login(username,password)
        self.csrf = re.findall('__csrf=(.*?) for', str(self.session.cookies))[0]
        self.Encrypt = Encrypt()
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
        }

    def GetPlayList(self):
        '''获得所有歌单'''
        playlist_url = 'https://music.163.com/weapi/user/playlist?csrf_token='
        playlists = []
        offset = 0
        while True:
            data = {
                "offset": offset,
                "uid": self.userid['userid'],
                "limit": 50,
                "csrf_token": self.csrf
            }
            res = self.session.post(playlist_url+self.csrf, headers=self.headers, data=self.Encrypt.get(data), verify=False)
            playlists += res.json()['playlist']
            offset += 1
            if res.json()['more'] == False:
                break
        all_playlists = {}
        for item in playlists:
            name = item.get('name')
            track_count = item.get('trackCount')
            play_count = item.get('playCount')
            play_id = item.get('id')
            if item.get('creator').get('userId') == self.userid:
                attr = '我创建的歌单'
            else:
                attr = '我收藏的歌单'
            all_playlists[str(play_id)] = [name, track_count, play_count, attr]
        return all_playlists

    def getPlayListSongs(self, playlist_id, num_songs):
        '''获得某歌单的所有歌曲信息'''
        detail_url = 'https://music.163.com/weapi/v6/playlist/detail?csrf_token='
        offset = 0
        song_infos = {}
        while True:
            data = {
                'id': playlist_id,
                'offset': offset,
                'total': True,
                'limit': 1000,
                'n': 1000,
                'csrf_token': self.csrf
            }
            res = self.session.post(detail_url+self.csrf, headers=self.headers, data=self.Encrypt.get(data),verify=False)
            tracks = res.json()['playlist']['tracks']
            for track in tracks:
                name = track.get('name')
                songid = track.get('id')
                artists = ','.join([i.get('name') for i in track.get('ar')])
                brs = [track.get('h')] + [track.get('m')] + [track.get('l')]
                song_infos[songid] = [name, artists, brs]
            offset += 1
            if len(list(song_infos.keys())) >= num_songs:
                break
            
        return song_infos

    def SetDownLoadPath(self,path):
        '''
        设置歌曲保存地址
        '''
        self.Download_path = path
    
    def SetMusicUrl(self, music_id):
        '''
        设置音乐地址
        '''
        self.music_url = "https://music.163.com/song?id=" + str(music_id)
    
    def RequestMusicParse(self):
        '''
        解析Music单曲数据
        '''
        Header = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-Requested-With": "XMLHttpRequest",
            "Content-Length": "92",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38"
        }
        payload = {
            "input": self.music_url,
            "filter": "url",
            "type": "_",
            "page": 1,
            "playUrl": ""
        }
        res = requests.post(self.req_url, headers = Header, data = urlencode(payload))
        res_content = res.text.encode('utf-8').decode()
        json_content = json.loads(res_content)
        if json_content["code"] == 200:
            self.music_title = json_content["data"][0]['title']      # music 标题
            self.music_author = json_content["data"][0]['author']    # music 作者
            print("     "+self.music_title+"-"+self.music_author+"     \n") 
            if "\"" in self.music_title or "\"" in self.music_author:
                self.music_title = self.music_title.replace('"','')
                self.music_author = self.music_author.replace('"','')
            self.music_lrc = json_content["data"][0]['lrc']          # music 歌词
            self.music_download_url = json_content["data"][0]['url'] # music 下载链接
            self.music_pic_url = json_content["data"][0]["pic"]      # music 图片链接
            return 1
        else:
            print("Parse failed!!!\n")
    
    def DownLoadMusic(self):
        '''
        下载音乐
        '''
        if self.music_download_url != None:
            print("正在下载music ->" + self.music_title)
            try:
                res = requests.get(self.music_download_url)
                if res.status_code == 200:
                    with open(self.Download_path+self.music_title+" - "+self.music_author+".mp3",'wb') as f:
                        f.write(res.content)
                    f.close()
                    print(self.music_title+" music Done")
                    return 0
            except:
                print("anything wrong\n")
                return -1    
        else:
            return -1

    def DownLoadLrc(self):
        '''
        下载歌词
        '''
        print("正在下载lrc ->" + self.music_title)
        with open(self.Download_path+self.music_title+" - "+self.music_author+".lrc",'w+',encoding="utf-8") as f:
            f.write(self.music_lrc)
        f.close()
        print(self.music_title+" lrc Done")


def parseArgs():
    '''
    命令行参数解析
    '''
    parser = argparse.ArgumentParser(description='下载网易云音乐登录用户创建/收藏的歌单内所有歌曲')
    parser.add_argument('--username', dest='username', help='用户名', type=str, required=True)
    parser.add_argument('--password', dest='password', help='密码', type=str, required=True)
    return parser.parse_args()



def run(username, password):
    print("-----------------------------------------------------------------------------")
    print("|                          Design By 锡城筱凯                               |")
    print("|                             2021.12.19                                    |")
    print("-----------------------------------------------------------------------------")
    
    md=MusicDownLoad(username, password)
    all_playlists = md.GetPlayList()
    playlistskeys= list(all_playlists.keys())
    for i in range(len(playlistskeys)):
        print(playlistskeys[i]+ "<->" +all_playlists[playlistskeys[i]][0]+ "<->" +str(all_playlists[playlistskeys[i]][1])+"首")
    playlist_id = input("输入想要下载的歌单ID:")
    song_infos = md.getPlayListSongs(playlist_id, all_playlists[playlist_id][1])
    song_ids=list(song_infos.keys())
    md.SetDownLoadPath(os.getcwd()+"\\")
    print("---------------------一共"+(str(len(song_ids)))+"个----------------------")
    success_num = 0
    failed_num = 0
    for i in range(len(song_ids)):
        print("---------------------第"+str(i+1)+"个开始---------------------")
        md.SetMusicUrl(song_ids[i])
        if(md.RequestMusicParse()):
            if md.DownLoadMusic() == 0:
                md.DownLoadLrc()
                success_num+=1
            else:
                failed_num+=1
            print("---------------------第"+str(i+1)+"个结束---------------------")
            print("---------------------还剩"+(str(len(song_ids)-i-1))+"个----------------------")
    print("---------------------成功"+str(success_num)+"个---------------------")
    print("---------------------失败"+str(failed_num)+"个---------------------")



args = parseArgs()
run(args.username,args.password)
